#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <errno.h>
#include "thread.h"
#include <sys/time.h>

int SIZE;

#define TIME_DIF( T1, T2 ) ((T2.tv_sec - T1.tv_sec) * 1000000L + (T2.tv_usec - T1.tv_usec)) / 1000000.


int** mat_A;
int** mat_B;
int** mat_C;

void initMat(int** matrice)
{
  int i, j, k = 0;
  for (i=0;i<SIZE;i++)
    {
      for (j=0;j<SIZE;j++)
	{
	  matrice[i][j] = k++;
	}
    }
}

void multMatSeq(int i)
{
 int j,k;
  for(j= 0; j<SIZE; j++){
    for(k= 0; k<SIZE; k++){   
      mat_C[i][j] +=mat_A[i][k] * mat_B[k][j]; 
    }
  }
}

void multMat(int i)
{
  int j,k;
  for(j= 0; j<SIZE; j++){
    for(k= 0; k<SIZE; k++){   
      mat_C[i][j] +=mat_A[i][k] * mat_B[k][j]; 
      //thread_yield();
    }
  }
  thread_exit(NULL);    
} 
int main(int argc, char** argv)
{
  SIZE = atoi(argv[1]);

  struct timeval tv1, tv2;
  int i = 0;
  mat_A = malloc(sizeof(int*) * SIZE);
  mat_B = malloc(sizeof(int*) * SIZE);
  mat_C = malloc(sizeof(int*) * SIZE);
  for (i=0;i<SIZE;i++)
    {
      mat_A[i] = malloc(sizeof(int) * SIZE);
      mat_B[i] = malloc(sizeof(int) * SIZE);
      mat_C[i] = malloc(sizeof(int) * SIZE);
    }
  initMat(mat_A);
  initMat(mat_B);
  thread_t threadArray[SIZE];//*
  gettimeofday(&tv1, NULL);
  for (i=0;i<SIZE;i++)
    {
      thread_create(&(threadArray[i]), multMat, (void*)i); 
    }
  for (i=0;i<SIZE;i++)
    {
      thread_join(threadArray[i], NULL);
    }
  gettimeofday(&tv2, NULL);
  printf("%f\n", TIME_DIF(tv1,tv2)); 
  gettimeofday(&tv1, NULL);


  for(i=0;i<SIZE;i++)
    {
      multMatSeq(i);
    }

  gettimeofday(&tv2, NULL);
  printf("%f\n", TIME_DIF(tv1,tv2));
  
  pthread_t pthread_array[SIZE];
   gettimeofday(&tv1, NULL);
   for(i=0;i<SIZE;i++)
     {
       int rt = pthread_create(&(pthread_array[i]), NULL, multMatSeq, (void*)i);
       if (rt != 0)
	 {
	   if (rt == EAGAIN)
	     printf("EAGAIN\n");
	   else if (rt == EINVAL)
	     printf("EINVAL\n");
	   else if (rt == EPERM)
	     printf("EPERM\n");
	   printf("ERROR : %d \n", i);
	   fflush(stdout);
	 }
     }
   for (i=0;i<SIZE;i++)
     {
       pthread_join(pthread_array[i], NULL);
     }
   gettimeofday(&tv2, NULL);
  printf("%f\n", TIME_DIF(tv1,tv2));
 
  for (i=0;i<SIZE;i++)
  {
    free(mat_A[i]);
    free(mat_B[i]);
    free(mat_C[i]);
  }
  free(mat_A);
  free(mat_B);
  free(mat_C);
  return 0;  
}
